
/**  
* @Title: HttpClientUtil.java
* @Package com.utils.client
* @Description: 
* @author likai
* @date 2017年5月3日
* @version V1.0  
*/

package com.utils.http;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.supers.enums.Charset;
import com.supers.pojo.HttpRequest;
import com.utils.transtype.JsonObjectUtil;

import sun.misc.BASE64Encoder;

/**
 * @ClassName: HttpClientUtil
 * @Description: httpclient发送get，post请求
 * @author likai
 * @date 2017年5月3日
 *
 */
@SuppressWarnings({ "deprecation", "restriction" })
public class HttpClientUtil{
	public static Logger log = LoggerFactory.getLogger(HttpClientUtil.class);
	private String contentType;
	private final static String json = "application/json;charset=";// 参数用字符串流
	private final static String form = "application/x-www-form-urlencoded;charset=";// 参数用form键值对


	public static HttpClientUtil httpJson = new HttpClientUtil(json);
	public static HttpClientUtil httpForm = new HttpClientUtil(form);
	
	private static int maxConnTotal = 200;// 所有路由总的最大链接数，默认200
	private static int maxConnPerRoute = 40;// 每个路由上的最大链接数，默认20
	private static int socketTimeout = 60000;// 从服务器获取响应数据的超时时间 ，单位毫秒,默认30s
	private static int connectTimeout = 30000;// 设置与服务器建立连接超时时间，单位毫秒,默认30s
	private static int connectionRequestTimeout = 10000;// 从连接池中获取连接的超时时间，单位毫秒,默认5s
	private static int failTryTime = 3;// 失败重试次数，默认三次
	/**
	 * HTTP连接池，该连接管理器必须是线程安全的.
	 */
	private static PoolingHttpClientConnectionManager connectionManager;
	private static CloseableHttpClient httpClient;

	private HttpClientUtil(String contentType) {
		super();
		this.contentType = contentType;
	}

	/*
	 * @Description:post请求
	 * 
	 * @param: 是否必须
	 * 
	 * 请求地址：url 是 请求参数：parameter 是 参数编码：charset 是
	 * 返回数据类型：responseClazz(null则返回null) 否
	 */

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public <T> T postClient(String url, Object parameter, Charset charset, Class<T> responseClazz)
			throws Exception {
		log.info("------------parameter--------------"+parameter);
		String result;
		HttpPost postRequest = new HttpPost(url);
		// 超时设置
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectTimeout)// 设置与服务器建立连接超时时间，单位毫秒
				.setSocketTimeout(socketTimeout)// 从服务器获取响应数据的超时时间 ，单位毫秒
				.setConnectionRequestTimeout(connectionRequestTimeout)// 从连接池中获取连接的超时时间
																		// ，单位毫秒
				.build();
		postRequest.setConfig(requestConfig);
		postRequest.setHeader("Content-Type", contentType + Charset.getCharset(charset));
		if (parameter!=null) {
			if (contentType.equals(json)) {
				// 字符串流传递
				String jsonData = null;
				if (parameter instanceof String) {
					jsonData = (String) parameter;
				} else {
					jsonData = JsonObjectUtil.ALWAYS.objectToJson(parameter);
				}
				StringEntity input = new StringEntity(jsonData, Charset.getCharset(charset));
				postRequest.setEntity(input);
			} else {
				// 键值对传递
				Map<String, Object> map =JsonObjectUtil.NON_NULL.beanToMap(parameter);
				List<NameValuePair> list = new ArrayList<NameValuePair>();
				Iterator iterator = map.entrySet().iterator();
				while (iterator.hasNext()) {
					Entry<String, String> elem = (Entry<String, String>) iterator.next();
					list.add(new BasicNameValuePair(elem.getKey(), elem.getValue()));
				}
				if (list.size() > 0) {
					UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, Charset.getCharset(charset));
					postRequest.setEntity(entity);
				}
			}
		}	
		CloseableHttpResponse response = httpClient.execute(postRequest);
		HttpEntity httpEntity = response.getEntity();
		result = EntityUtils.toString(httpEntity, Charset.getCharset(charset));// 取出应答字符串
		result.replaceAll("\r", "");// 去掉返回结果中的"\r"字符，否则会在结果字符串后面显示一个小方格
		if (("".equals(result)) || (result == null)) {
			return null;
		}

		log.info(result);
		if (String.class.equals(responseClazz)) {
			return (T) result;
		}
		if (responseClazz != null) {
			return JsonObjectUtil.ALWAYS.jsonToObject(result, responseClazz);
		} else {
			return null;
		}
	}

	/*
	 * @Description:post请求返回文件
	 * 
	 * @param: 是否必须
	 * 
	 * 请求地址：url 是 请求参数：parameter 是 参数编码：charset 是
	 * 返回数据类型：responseClazz(null则返回null) 否
	 */

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public File postClientReturnFile(String url, Object parameter, Charset charset, String filePath) throws Exception {
		log.info("------------parameter--------------"+parameter);
		HttpPost postRequest = new HttpPost(url);
		// 超时设置
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectTimeout)// 设置与服务器建立连接超时时间，单位毫秒
				.setSocketTimeout(socketTimeout)// 从服务器获取响应数据的超时时间 ，单位毫秒
				.setConnectionRequestTimeout(connectionRequestTimeout)// 从连接池中获取连接的超时时间
																		// ，单位毫秒
				.build();
		postRequest.setConfig(requestConfig);
		postRequest.setHeader("Content-Type", contentType + Charset.getCharset(charset));
		OutputStream fileOut = null;
		if (parameter!=null) {
			if (contentType.equals(json)) {
				// 字符串流传递
				String jsonData = null;
				if (parameter instanceof String) {
					jsonData = (String) parameter;
				} else {
					jsonData = JsonObjectUtil.ALWAYS.objectToJson(parameter);
				}
				StringEntity input = new StringEntity(jsonData, Charset.getCharset(charset));
				postRequest.setEntity(input);
			} else {
				// 键值对传递
				Map<String, Object> map = JsonObjectUtil.NON_NULL.beanToMap(parameter);
				List<NameValuePair> list = new ArrayList<NameValuePair>();
				Iterator iterator = map.entrySet().iterator();
				while (iterator.hasNext()) {
					Entry<String, String> elem = (Entry<String, String>) iterator.next();
					list.add(new BasicNameValuePair(elem.getKey(), elem.getValue()));
				}
				if (list.size() > 0) {
					UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, Charset.getCharset(charset));
					postRequest.setEntity(entity);
				}
			}
		}
		try {
			CloseableHttpResponse response = httpClient.execute(postRequest);
			HttpEntity httpEntity = response.getEntity();
			if (httpEntity.getContentType().getValue().contains("application/json")) {
				// 返回数据非文件
				log.error(EntityUtils.toString(httpEntity, Charset.getCharset(charset)));
				return null;
			}
			File file = new File(filePath);
			fileOut = new FileOutputStream(file);
			httpEntity.writeTo(fileOut);
			return file;
		} finally {
			if (fileOut != null) {
				fileOut.close();
			}
		}
	}

	/*
	 * @Description:post请求返回文件base64码
	 * 
	 * @param: 是否必须
	 * 
	 * 请求地址：url 是 请求参数：parameter 是 参数编码：charset 是
	 * 返回数据类型：responseClazz(null则返回null) 否
	 */

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public String postClientReturnBase64(String url, Object parameter, Charset charset) throws Exception {
		log.info("------------parameter--------------"+parameter);
		HttpPost postRequest = new HttpPost(url);
		// 超时设置
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectTimeout)// 设置与服务器建立连接超时时间，单位毫秒
				.setSocketTimeout(socketTimeout)// 从服务器获取响应数据的超时时间 ，单位毫秒
				.setConnectionRequestTimeout(connectionRequestTimeout)// 从连接池中获取连接的超时时间
																		// ，单位毫秒
				.build();
		postRequest.setConfig(requestConfig);
		postRequest.setHeader("Content-Type", contentType + Charset.getCharset(charset));
		InputStream inputStream = null;
		ByteArrayOutputStream bos =null;
		if (parameter!=null) {
			if (contentType.equals(json)) {
				// 字符串流传递
				String jsonData = null;
				if (parameter instanceof String) {
					jsonData = (String) parameter;
				} else {
					jsonData = JsonObjectUtil.ALWAYS.objectToJson(parameter);
				}
				StringEntity input = new StringEntity(jsonData, Charset.getCharset(charset));
				postRequest.setEntity(input);
			} else {
				// 键值对传递
				Map<String, Object> map =JsonObjectUtil.NON_NULL.beanToMap(parameter);
				List<NameValuePair> list = new ArrayList<NameValuePair>();
				Iterator iterator = map.entrySet().iterator();
				while (iterator.hasNext()) {
					Entry<String, String> elem = (Entry<String, String>) iterator.next();
					list.add(new BasicNameValuePair(elem.getKey(), elem.getValue()));
				}
				if (list.size() > 0) {
					UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, Charset.getCharset(charset));
					postRequest.setEntity(entity);
				}
			}
		}
		try {
			CloseableHttpResponse response = httpClient.execute(postRequest);
			HttpEntity httpEntity = response.getEntity();
			if (httpEntity.getContentType().getValue().contains("application/json")) {
				// 返回数据非文件
				log.error(EntityUtils.toString(httpEntity, Charset.getCharset(charset)));
				return null;
			}
			try {
				inputStream = httpEntity.getContent();
				byte[] buffer = new byte[1024];
				int len = 0;
				bos = new ByteArrayOutputStream();
				while ((len = inputStream.read(buffer)) != -1) {
					bos.write(buffer, 0, len);
				}
				byte[] data = bos.toByteArray();
				// 加密
				BASE64Encoder encoder = new BASE64Encoder();
				return encoder.encode(data);
			} catch (IOException e) {
				e.printStackTrace();
				return null;
			}
		} finally {
			if (inputStream != null) {
				inputStream.close();
			}
			if (bos != null) {
				bos.close();
			}
		}
	}

	/*
	 * @Description:get请求
	 * 
	 * @param: 是否必须
	 * 
	 * 请求地址：url(包含参数) 是 请求参数：parameter 是 参数编码：charset 是
	 * 返回数据类型：responseClazz(null则返回null) 否
	 */

	@SuppressWarnings("unchecked")
	public <T> T getClient(String url, Charset charset, Class<T> responseClazz) throws Exception {
		String result;
		HttpGet getRequest = new HttpGet(url);
		// 超时设置
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectTimeout)// 设置与服务器建立连接超时时间，单位毫秒
				.setSocketTimeout(socketTimeout)// 从服务器获取响应数据的超时时间 ，单位毫秒
				.setConnectionRequestTimeout(connectionRequestTimeout)// 从连接池中获取连接的超时时间
																		// ，单位毫秒
				.build();
		getRequest.setConfig(requestConfig);
		getRequest.setHeader("Content-Type", contentType + Charset.getCharset(charset));
		
		CloseableHttpResponse response = httpClient.execute(getRequest);

		HttpEntity httpEntity = response.getEntity();
		result = EntityUtils.toString(httpEntity, Charset.getCharset(charset));// 取出应答字符串
		result.replaceAll("\r", "");// 去掉返回结果中的"\r"字符，否则会在结果字符串后面显示一个小方格
		if (("".equals(result)) || (result == null)) {
			return null;
		}
		if (String.class.equals(responseClazz)) {
			return (T) result;
		}
		if (responseClazz != null) {
			return JsonObjectUtil.ALWAYS.jsonToObject(result.toString(), responseClazz);
		} else {
			return null;
		}
	}

	/**
	 * 初始化数据.
	 */

	static {
		
		//采用绕过验证的方式处理https请求  
		/*SSLContext sslcontext=null;
		try {
			sslcontext = createIgnoreVerifySSL();
		} catch (KeyManagementException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} 
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()  
            .register("http", PlainConnectionSocketFactory.INSTANCE)  
            .register("https", new SSLConnectionSocketFactory(sslcontext))  
            .build();
        connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);*/
		
        //连接池配置
		connectionManager = new PoolingHttpClientConnectionManager();
		connectionManager.setMaxTotal(maxConnTotal);// 设置整个连接池最大连接数
		connectionManager.setDefaultMaxPerRoute(maxConnPerRoute);// 设置每个服务分配线程数（可MaxTotal/服务数来设置）
		RequestConfig requestConfig=RequestConfig.custom().setConnectTimeout(connectTimeout)
				.setSocketTimeout(socketTimeout)
				.setConnectionRequestTimeout(connectionRequestTimeout)
				.build();
		HttpClientBuilder builder=HttpClientBuilder.create();
		builder.setConnectionManager(connectionManager);
		builder.setDefaultRequestConfig(requestConfig);
		
		HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
			@Override
			public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
				if (executionCount >= failTryTime) {
					// Do not retry if over max retry count
					return false;
				}
				if (exception instanceof InterruptedIOException) {
					// Timeout
					return false;
				}
				if (exception instanceof UnknownHostException) {
					// Unknown host
					return false;
				}
				if (exception instanceof ConnectException) {
					// Connection refused
					return false;
				}
				if (exception instanceof SSLException) {
					// SSL handshake exception
					return false;
				}
				HttpRequest request = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
				boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
				if (idempotent) {
					// Retry if the request is considered idempotent
					return true;
				}
				return false;
			}

		};
		builder.setRetryHandler(myRetryHandler);//设置重试第一个为重试次数，第二个为是否开启重试

		httpClient=builder.build();
	}


	// 绕过https验证
	public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
		SSLContext sc = SSLContext.getInstance("SSLv3");
		// 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
		X509TrustManager trustManager = new X509TrustManager() {
			@Override
			public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException {
			}

			@Override
			public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException {
			}

			@Override
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}
		};

		sc.init(null, new TrustManager[] { trustManager }, null);
		return sc;
	}
	public static void main(String[] args) throws Exception {
		System.out.println(HttpClientUtil.httpForm.getClient("http://localhost:8608/tx/manager/getServer", Charset.UTF8, String.class));
	}
}
